/* eslint-disable camelcase */
import React, { Component } from 'react';
import { CancelToken } from 'axios';
import { Radio, Icon, Popover, Button, Upload, Modal, message, Progress, Tooltip } from 'antd';
import {
  getPackageInfo,
  getUpdate,
  uploadClanPackage,
  cancelSystemUpgrade,
  clusterUpgrade,
  getUpgradeUpdate,
  getClusterAutoUpdate,
  clusterAutoUpdate
} from '@api/update';
import ClusterDeviceUpdate from './components/cluster-device-update';
import { getNetConfig } from '@api/netConfig';
import arrowDownImg from './images/arrow-down.svg';
import UpgradingImg from './images/upgrading.png';
import ComputerImg from './images/computer.png';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import cn from 'classnames';
import _ from 'lodash';
import { UPDATING_STAGE_PROCESS_MAP, MAX_UPDATING_ERROR_TIMES } from '@util/constants';
import './index.less';
import ResourceUpdate from './components/resource-update';

const prefixCls = 'SystemUpdate';
const RadioGroup = Radio.Group;

const typeDist = {
  single: '',
  slave: '扫描节点',
  master: '管理节点'
};
const getSize = bytes => {
  if (bytes < 0) {
    return '0B';
  }
  const units = ['B', 'K', 'M', 'G'];
  for (let level = 0; level < units.length; level++) {
    if (bytes < Math.pow(1024, level + 1) || level === units.length - 1) {
      const unit = units[level];
      const prefix = parseInt((100 * bytes) / Math.pow(1024, level), 10) / 100;
      return `${prefix}${unit}`;
    }
  }
};

const getTime = rawSecond => {
  const second = parseInt(rawSecond % 60, 10).toString();
  const minutes = parseInt((rawSecond % (60 * 60)) / 60, 10).toString();
  const hours = parseInt(rawSecond / (60 * 60), 10).toString();
  return [
    hours.length >= 2 ? hours : `00${hours}`.slice(-2),
    `00${minutes}`.slice(-2),
    `00${second}`.slice(-2)
  ].join(':');
};

const radioStyle = {
  display: 'block',
  height: '30px',
  lineHeight: '30px'
};
class SystemUpdate extends Component {
  static propTypes = {
    deviceId: PropTypes.string,
    cert: PropTypes.object
  };
  constructor(props) {
    super(props);
    const deviceId = _.get(props, 'deviceId', '');
    this.state = {
      packageFile: null,
      manual: false,
      failModalVisible: false,
      modeVisible: false,
      mode: '',
      edition: '',
      lastUpgrade: '',
      saveLoading: false,
      percent: 0,
      deviceId: deviceId,
      errorMsg: '',
      updateWay: 'auto',
      verifyModalVisible: false,
      verify: false,
      upgrading: false,
      upgradeProcess: 0,
      pkginfo: {},
      sendUpgrading: false,
      uploadingPackage: false,
      workIp: '',
      uploadModalVisible: false,
      bytesPerSecond: 0, // 每秒上传的字节数
      remainUploadSeconds: 0, // 剩余上传时间,秒
      nodeList: [],
      clusterSpinning: false,
      updatedSameVersionPkgAtCluster: false, // 集群环境下上传了相同版本号的升级包
      maxManual: false,
      isCluster: false,
      nodesProgress: [],
      systemUpdateNode: true,
      node: null,
      resourceVisible: false,
      uploadErrorMsg: '请确认是上传了同版本、跨版本或者无效的升级包'
    };
    this.lastRecordLoaded = 0;
    this.lastRecordTime = 0;
    this.errorTimes = 0;
    this.pollTimer = null;
    this.nodesProgressTimer = null;
    this.clusterListStatust = null;
    this._cancelUpload = null;
    this._isCanceled = false;
    this._maxisCanceled = false;
  }

  componentDidMount() {
    const { cert } = this.props;
    const { modules } = cert;
    const isCluster = _.includes(modules, 'distribute') && _.get(cert, 'is_cluster', false);
    this.setState({ isCluster: isCluster });
    // 设置本地ip为工作口ipv4地址
    getNetConfig().then(res => {
      let list = [];
      try {
        list = JSON.parse(_.get(res.data.data.result, 'message', []));
        // eslint-disable-next-line
      } catch (e) {}
      list.forEach(item => {
        if (item?.name?.includes('work')) {
          this.setState({
            workIp: item.address_ipv4
          });
        }
      });
    });
    // 获取系统更新数据
    getUpdate()
      .then(async res => {
        const modeStatus = _.get(res.data.data, 'update.auto', false);
        const lastUpgrade = _.get(res.data.data, 'update.lastUpgrade', '');
        const mode = modeStatus ? 'auto' : 'manual';
        this.setState({
          mode,
          manual: !modeStatus,
          lastUpgrade,
          updateWay: mode
        });
        // 集群情况下更换更新方式数据
        let status = await getClusterAutoUpdate({ update_type: 'system' });
        this.setState({
          mode: _.get(status, 'data.data.auto_upgrade_status') === 'inactive' ? 'manual' : 'auto'
        });
      })
      .catch(() => {
        // eslint-disable-next-line
        console.error('获取更新方式失败');
      });

    // 获取系统升级包版本
    getPackageInfo()
      .then(res => {
        const edition = _.get(res.data.data, 'current', '');
        this.setState({ edition });
      })
      .catch(() => {
        // eslint-disable-next-line
        console.error('获取升级包信息失败');
      });
    // 集群的话轮训去查状态
    this.pollingProgress();
    this.setState({ clusterSpinning: true });
  }

  componentWillUnmount() {
    window.clearInterval(this.pollTimer);
    window.clearInterval(this.nodesProgressTimer);
  }

  onRadioChange = e => {
    const value = e.target.value;
    if (value === 'manual') {
      this.setState({ manual: true, mode: 'manual' });
      return;
    }
    this.setState({ manual: false, mode: 'auto' });
  };

  onSave = () => {
    const { manual } = this.state;
    this.setState({ saveLoading: true });
    let api = clusterAutoUpdate;
    let body = { auto: !manual ? 'start' : 'stop', update_type: 'system' };

    api(body)
      .then(() => {
        message.success('保存升级方式成功');
        this.setState({
          saveLoading: false,
          modeVisible: false,
          updateWay: manual ? 'manual' : 'auto'
        });
      })
      .catch(() => {
        message.error('保存升级方式失败');
        this.setState({ saveLoading: false });
      });
  };

  beforeUpload = packageFile => {
    this.setState({ packageFile }, this.uploadUpdate);
    return false;
  };

  uploadUpdate = () => {
    const { packageFile, edition } = this.state;
    const body = new FormData();
    body.append('file', packageFile);
    let config = {
      onUploadProgress: progressEvent => {
        if (!this.state.uploadingPackage) {
          return;
        }
        let loaded = progressEvent.loaded;
        let total = progressEvent.total;
        let percent = ((loaded / total) * 100) | 0;
        let now = Date.now();
        let bytesPerSecond =
          (1000 * (loaded - this.lastRecordLoaded)) / (now - this.lastRecordTime);
        let remainUploadSeconds = (total - loaded) / bytesPerSecond;
        this.lastRecordTime = now;
        this.lastRecordLoaded = loaded;
        this.setState({
          percent,
          bytesPerSecond,
          remainUploadSeconds
        });
      },
      cancelToken: new CancelToken(cancel => {
        this._cancelUpload = cancel;
      })
    };
    this.setState({ percent: 0, uploadingPackage: true });
    this.lastRecordLoaded = 0;
    this.lastRecordTime = Date.now();
    this._isCanceled = false;
    body.append('update_type', 'system');
    let api = uploadClanPackage;
    api(body, config)
      .then(res => {
        const pkginfo = _.get(res, 'data.data.result.pkginfo', {});
        const pkgVersion = _.get(pkginfo, 'version', '');
        const updatedSameVersionPkgAtCluster = edition === pkgVersion;
        const verify =
          _.get(res.data.data, 'result.verify') === 'success' ||
          (_.get(res.data.data, 'result.master_ret.verify') === 'success' &&
            edition !== pkgVersion);
        this.setState({
          verifyModalVisible: true,
          verify,
          pkginfo,
          percent: 100,
          uploadingPackage: false,
          updatedSameVersionPkgAtCluster,
          uploadModalVisible: false
        });
      })
      .catch(e => {
        if (!this._isCanceled) {
          this.setState({
            uploadErrorMsg: e.message || '请确认是上传了同版本、跨版本或者无效的升级包',
            uploadingPackage: false,
            uploadModalVisible: false,
            verifyModalVisible: true,
            verify: false
          });
        } else {
          this.setState({ uploadingPackage: false });
        }
      });
  };

  onRemove = () => {
    this.setState({ packageFile: null });
  };

  setModalVisible = failModalVisible => {
    this.setState({ failModalVisible });
  };

  setModeModaleVisible = visible => {
    this.setState({ modeVisible: visible });
  };

  upgrade = () => {
    const { pkginfo, updatedSameVersionPkgAtCluster } = this.state;
    if (updatedSameVersionPkgAtCluster) {
      // 集群上传相同版本
      Modal.success({
        title: '更新任务同步成功',
        content:
          '已将更新任务同步到引擎设备，如存在引擎设备为前一个版本，将会立即升级。请您5-15分钟后再查看引擎设备升级情况。'
      });
      this.setState({ verifyModalVisible: false });
    } else if (_.get(pkginfo, 'version')) {
      this.setState({ sendUpgrading: true });
      let api = clusterUpgrade;
      let body = { update_type: 'system', vision: pkginfo.version };

      api(body)
        .then(() => {
          this.errorTimes = 0;
          this.setState(
            {
              verifyModalVisible: false,
              upgrading: true,
              upgradeProcess: 0,
              packageFile: null,
              sendUpgrading: false
            },
            () => {
              // 轮训请求集群信息
              this.pollStatus();
            }
          );
        })
        .catch(() => {
          this.setState({
            failModalVisible: true,
            verifyModalVisible: false,
            sendUpgrading: false
          });
        });
    } else {
      this.setState({ failModalVisible: true, verifyModalVisible: false });
    }
  };

  // 取消上传
  cancelSystemUpgrade = () => {
    const { pkginfo, packageFile } = this.state;
    let pkgname = _.get(packageFile, 'name', '');
    cancelSystemUpgrade({ pkglog: pkginfo.version, pkgname })
      .then(() => {
        message.success('上传成功');
      })
      .catch(() => {
        message.error('上传失败');
      })
      .finally(() => {
        this.setState({ verifyModalVisible: false });
      });
  };

  // 集群请求的升级
  getUpgradeUpdate = () => {
    const { workIp } = this.state;
    getUpgradeUpdate()
      .then(res => {
        const data = _.get(res, 'data', []);
        let item = data.find(r => r.ip === workIp && (r.role === 'master' || r.role === 'single'));
        if (!item) {
          return;
        }
        let stage = _.get(item, 'stage');
        let expire = Number(_.get(item, 'expire', 0)) * 1000;
        let status = _.get(item, 'status');
        if (!status) {
          if (expire < Date.now()) {
            this.errorTimes++;
          } else {
            this.errorTimes = 0;
          }
          if (this.errorTimes > MAX_UPDATING_ERROR_TIMES) {
            status = 'fail';
          } else if (this.errorTimes) {
            return;
          }
        }
        if (status === 'success') {
          // 升级成功
          this.setState({
            upgradeProcess: 100
          });
          message.success('升级成功!');
          window.clearInterval(this.pollTimer);
          setTimeout(() => {
            this.setState({ upgrading: false });
            window.location.reload();
          }, 3000);
        } else if (status === 'failed') {
          // 升级失败
          window.clearInterval(this.pollTimer);
          this.setState({
            upgradeProcess: 100,
            failModalVisible: true,
            upgrading: false
          });
          window.clearInterval(this.pollTimer);
        } else if (UPDATING_STAGE_PROCESS_MAP[stage]) {
          this.setState({
            upgradeProcess: UPDATING_STAGE_PROCESS_MAP[stage]
          });
        }
      })
      .catch(error => {
        this.errorTimes++;
        if (this.errorTimes > MAX_UPDATING_ERROR_TIMES) {
          // 升级失败
          this.setState({
            upgradeProcess: 100,
            status: 'exception',
            upgrading: false,
            desc: '升级失败!'
          });
          window.clearInterval(this.pollTimer);
        }
        window.console.log(error);
      });
  };

  getUpgradeState = () => {
    this.getUpgradeUpdate();
  };

  pollStatus() {
    this.pollTimer = window.setInterval(this.getUpgradeState, 3000);
  }

  loadProgress = () => {
    const { edition } = this.state;
    getUpgradeUpdate().then(res => {
      const nodesProgress = _.get(res, 'data', []);
      this.setState({ nodesProgress: nodesProgress });
      const node = _.find(nodesProgress, i => i.ip === edition);
      let item = nodesProgress.find(r => r.status === 'updating');
      this.setState({ node: item });
      if (!node) {
        if (item) {
          if (item?.expire * 1000 > Date.now()) {
            this.setState({ systemUpdateNode: false });
          }
          return;
        }
      }
      this.setState({ systemUpdateNode: true });
    });
  };

  pollingProgress = () => {
    this.nodesProgressTimer = window.setInterval(this.loadProgress, 4000);
  };

  cancelUpload = () => {
    this.setState({
      packageFile: null
    });
    this._isCanceled = true;
    if (this._cancelUpload instanceof Function) {
      this._cancelUpload();
    }
  };

  getUpdateWay = () => {
    const { cert } = this.props;
    const { updateWay } = this.state;
    const now = Number(_.get(cert, 'now', 0));
    const isExpired = Number(_.get(cert, 'expiry_time', 0)) * 1000 < now;

    if (isExpired) {
      return '授权证书已过期，暂时无法升级';
    }
    if (updateWay === 'auto') {
      return '自动升级，每10分钟访问一次升级服务器（如您的服务器不能访问外网，请选择手动升级）';
    } else {
      return '手动升级';
    }
  };

  render() {
    const {
      uploadErrorMsg,
      packageFile,
      failModalVisible,
      mode,
      edition,
      lastUpgrade,
      saveLoading,
      percent,
      deviceId,
      modeVisible,
      errorMsg,
      verifyModalVisible,
      verify,
      upgrading,
      upgradeProcess,
      pkginfo,
      sendUpgrading,
      uploadModalVisible,
      workIp,
      remainUploadSeconds,
      bytesPerSecond,
      uploadingPackage,
      nodeList,
      // clusterSpinning,
      updatedSameVersionPkgAtCluster,
      isCluster,
      nodesProgress,
      systemUpdateNode,
      node,
      resourceVisible
    } = this.state;
    const { cert } = this.props;
    const now = Number(_.get(cert, 'now', 0));
    const expired = Number(_.get(cert, 'expiry_time', 0)) * 1000 < now;
    const enableSensitivePage = _.get(cert, 'enable_sensitive_page', true);
    const content = (
      <div>
        <b>自动升级：</b>请确保您的网络环境能访问外网。
        <br />
        <br />
        <b>手动升级：</b>如您的服务器不能访问外网，请选择手动升级。
      </div>
    );
    const verifyModalProps = verify
      ? {
          onOk: this.upgrade,
          onCancel: this.cancelSystemUpgrade,
          okText: '开始更新',
          cancelText: '暂不更新'
        }
      : {
          footer: [
            <Button
              type="primary"
              key="0"
              onClick={() => this.setState({ verifyModalVisible: false, packageFile: null })}
            >
              关闭
            </Button>
          ]
        };

    return (
      <>
        <div className={`${prefixCls}-wrapper`}>
          {isCluster && <div className={`${prefixCls}-page-title`}>系统升级</div>}
          <div className={prefixCls} style={{ height: isCluster ? '600px' : '100%' }}>
            <div className={`${prefixCls}-page-title`}>{isCluster ? '本机升级' : '系统升级'}</div>
            <div className={`${prefixCls}-top`}>
              <div className="left">
                <img src={ComputerImg} className="icon" />
                <div className="info">
                  <div className="ip">{workIp || '-'}</div>
                  <div className="desc">本机</div>
                </div>
                <Tooltip
                  visible={resourceVisible && !systemUpdateNode}
                  title={`${typeDist[(node?.role)]}${node?.ip}正在进行${
                    node?.update_type === 'sysmg' ? '系统' : '资源库'
                  }升级，建议不要同时升级，请稍后重试`}
                  disabled={true}
                  style={{ background: 'red' }}
                >
                  {/* 这里不加span就无法执行onMouseOver */}
                  <span>
                    <Button
                      type="primary"
                      disabled={!systemUpdateNode}
                      onMouseOver={() => this.setState({ resourceVisible: true })}
                      onMouseOut={() => this.setState({ resourceVisible: false })}
                      onClick={() =>
                        this.setState({
                          uploadModalVisible: true,
                          packageFile: null,
                          uploadingPackage: false
                        })
                      }
                    >
                      上传升级包
                    </Button>
                  </span>
                </Tooltip>
              </div>
              <div className="right">
                <div className={`${prefixCls}-item`}>
                  <div className={`${prefixCls}-item-title`}>当前版本</div>
                  <div className={`${prefixCls}-item-detail`}>{edition}</div>
                </div>
                <div className={`${prefixCls}-item`}>
                  <div className={`${prefixCls}-item-title`}>最近更新时间</div>
                  <div className={`${prefixCls}-item-detail`}>{lastUpgrade}</div>
                </div>
                <div className={`${prefixCls}-item`}>
                  <Popover placement="topLeft" content={content} overlayStyle={{ width: 420 }}>
                    <div className={`${prefixCls}-item-title`}>
                      更新方式&nbsp;
                      <Icon type="info-circle" />
                    </div>
                  </Popover>
                  <div className={`${prefixCls}-item-detail`}>
                    <span>{this.getUpdateWay()}</span>
                    <span
                      // className={cn({ 'be-color': expired })}
                      style={{
                        color: expired ? 'rgba(0, 0, 0, 0.25)' : '#1890ff',
                        cursor: expired ? 'not-allowed' : 'pointer',
                        marginLeft: 16
                      }}
                      onClick={() => !expired && this.setModeModaleVisible(true)}
                      loading={saveLoading}
                    >
                      编辑升级方式
                    </span>
                  </div>
                </div>
              </div>
            </div>

            <div style={{ marginTop: 40 }}>
              <div>注：手动升级需要您上传升级文件，升级文件获取方式</div>
              {enableSensitivePage && (
                <div className={`${prefixCls}-manual-intro`}>
                  <div className={`${prefixCls}-manual-intro-step`}>
                    <div className={`${prefixCls}-manual-intro-step-num`}>1</div>
                    <div className={`${prefixCls}-manual-intro-step-arrow`}>
                      <img src={arrowDownImg} alt="" />
                    </div>
                    <div className={`${prefixCls}-manual-intro-step-num`}>2</div>
                    <div className={`${prefixCls}-manual-intro-step-arrow`}>
                      <img src={arrowDownImg} alt="" />
                    </div>
                    <div className={`${prefixCls}-manual-intro-step-num`}>3</div>
                  </div>
                  <div>
                    <div className={`${prefixCls}-manual-intro-msg`}>
                      先访问
                      <a
                        href="http://update.knownsec.com"
                        target="_blank"
                        rel="noopener noreferrer"
                      >
                        http://update.knownsec.com
                      </a>
                    </div>
                    <div className={`${prefixCls}-manual-intro-msg`}>
                      输入License Key：<span style={{ color: '#0b6fd6' }}>{deviceId}&nbsp;</span>
                      进行登录验证，成功登录后下载大于当前版本
                      <span style={{ color: '#0b6fd6' }}>&nbsp;{edition}&nbsp;</span>的所有升级文件
                    </div>
                    <div className={`${prefixCls}-manual-intro-msg`}>
                      在此页面点击“上传升级包”按钮，按照版本号从最小版本依次往上升级，直到升级到您最终想要的版本。
                    </div>
                  </div>
                </div>
              )}
            </div>
          </div>
          <ResourceUpdate
            lastSystemUpgrade={lastUpgrade}
            deviceId={deviceId}
            enableSensitivePage={enableSensitivePage}
            cert={cert}
            nodesProgressList={nodesProgress}
          />
          {isCluster && (
            <ClusterDeviceUpdate
              data={nodeList}
              edition={edition}
              nodesProgressList={nodesProgress}
            />
          )}
          <Modal
            maskClosable={false}
            className={cn(`${prefixCls}-upload-pkg-modal`, 'no-header-bottom-lines', 'padding-24')}
            visible={uploadModalVisible}
            title={uploadingPackage ? '安装包上传中' : '上传安装包'}
            footer={null}
            onCancel={() => this.setState({ uploadModalVisible: false })}
          >
            {!uploadingPackage && (
              <Upload.Dragger accept=".pkg" beforeUpload={this.beforeUpload} fileList={[]}>
                <div className="upload-wrapper">
                  <Icon type="upload" className="icon" width={32} height={32} />
                  <div className="desc">
                    <span>点击或拖拽至此处上传</span>
                    <span>仅支持 *.pkg 文件</span>
                  </div>
                </div>
              </Upload.Dragger>
            )}
            {packageFile && (
              <div className="update-info">
                <div className="top">
                  <Icon type="link" />
                  {_.get(packageFile, 'name', '-')}&nbsp;({getSize(_.get(packageFile, 'size', 0))})
                </div>
                <div className="bottom">
                  <Progress percent={percent} showInfo={false} />
                  <span className="speed">{`${getSize(bytesPerSecond)}/s`}</span>
                  <span className="remain">{getTime(remainUploadSeconds)}</span>
                  <span className="remove" onClick={this.cancelUpload}>
                    删除
                  </span>
                </div>
              </div>
            )}
          </Modal>

          <Modal
            maskClosable={false}
            className={cn(
              'mode-change-modal',
              'no-header-bottom-lines',
              'bottom-center',
              'padding-24'
            )}
            visible={modeVisible}
            title={'编辑升级方式'}
            confirmLoading={saveLoading}
            onOk={this.onSave}
            onCancel={() => this.setModeModaleVisible(false)}
          >
            <RadioGroup onChange={this.onRadioChange} value={mode}>
              <Radio value={'auto'} style={radioStyle}>
                自动升级,每2小时访问一次升级服务器
              </Radio>
              <Radio value={'manual'} style={radioStyle}>
                手动升级
              </Radio>
            </RadioGroup>
          </Modal>

          <Modal
            maskClosable={false}
            visible={failModalVisible}
            footer={null}
            onCancel={() => {
              this.setModalVisible(false);
              this.setState({ packageFile: null });
            }}
            style={{ textAlign: 'center' }}
          >
            <div>
              <Icon
                type="close-circle"
                theme="twoTone"
                twoToneColor="#F04038"
                style={{ fontSize: 32 }}
              />
              <h3 style={{ margin: 16 }}>升级失败</h3>
              {errorMsg}
              <br />
              <Button
                type="primary"
                style={{ marginTop: 16 }}
                onClick={() => {
                  this.setModalVisible(false);
                  this.setState({ packageFile: null });
                }}
              >
                知道了
              </Button>
            </div>
          </Modal>

          <Modal
            maskClosable={false}
            className={cn(
              `${prefixCls}-verify`,
              'no-header-bottom-lines',
              'bottom-center',
              'padding-24'
            )}
            closable={false}
            visible={verifyModalVisible}
            {...verifyModalProps}
            confirmLoading={sendUpgrading}
          >
            {verify ? (
              <div className="pkginfo">
                <div className="icon">
                  <img src={UpgradingImg} />
                </div>
                <div className="version">V{_.get(pkginfo, 'version', '-')}&nbsp;版本更新</div>
                <div className="content">
                  {Array.isArray(_.get(pkginfo, 'content')) && _.get(pkginfo, 'content.length')
                    ? pkginfo.content.map((i, index) => <p key={index}>{i}</p>)
                    : '-'}
                </div>
                {updatedSameVersionPkgAtCluster && (
                  <div className="cluster-same-version">
                    提示：此升级包版本和调度设备版本相同，如存在引擎设备为此前一个版本，请点击开始更新。如不存在，请选择暂不更新。
                  </div>
                )}
              </div>
            ) : (
              <div>
                <p>升级包上传失败!</p>
                <p style={{ marginBottom: 0 }}>{uploadErrorMsg}</p>
              </div>
            )}
          </Modal>

          <Modal
            maskClosable={false}
            visible={upgrading}
            footer={null}
            title={null}
            closable={false}
            className="upgrading-modal"
          >
            <img src={UpgradingImg} className="upgrading-img" />
            <Progress
              percent={upgradeProcess}
              style={{ width: 260, marginLeft: 16 }}
              status="active"
            />
            <p className="desc">请稍后，网站正在飞速升级中…</p>
          </Modal>
        </div>
      </>
    );
  }
}
const mapStateToProps = state => {
  return {
    deviceId: _.get(state, 'auth.deviceId', ''),
    cert: _.get(state, 'auth.cert', {})
  };
};

export default connect(mapStateToProps)(SystemUpdate);
